#!/usr/bin/env python
import PySimpleGUI as sg
import os
import psutil
import win32api
import win32con
import win32gui
import win32process
import cv2
from PIL import ImageGrab
import numpy as np


def convert_string_to_tuple(string):
    """
    Converts a string that represents a tuple.  These strings have the format:
    "('item 1', 'item 2')"
    The desired return value is ('item 1', 'item 2')
    :param string:
    :return:
    """
    parts = string[1:-1].split(',')
    part1 = parts[0][1:-1]
    part2 = parts[1][2:-1]
    return part1, part2


def show_list_by_name(window, output_key, python_only):
    process_list = get_window_list()

    title_list = []
    for proc in process_list:
        names = convert_string_to_tuple(proc)
        if python_only and names[0] == 'python.exe':
            title_list.append(names[1])
        elif not python_only:
            title_list.append(names[1])
    title_list.sort()
    window[output_key].update(title_list)
    return title_list


def get_window_list():
    titles = []
    t = []
    pidList = [(p.pid, p.name()) for p in psutil.process_iter()]

    def enumWindowsProc(hwnd, lParam):
        """ append window titles which match a pid """
        if (lParam is None) or ((lParam is not None) and (win32process.GetWindowThreadProcessId(hwnd)[1] == lParam)):
            text = win32gui.GetWindowText(hwnd)
            if text:
                wStyle = win32api.GetWindowLong(hwnd, win32con.GWL_STYLE)
                if wStyle & win32con.WS_VISIBLE:
                    t.append("%s" % (text))
                    return

    def enumProcWnds(pid=None):
        win32gui.EnumWindows(enumWindowsProc, pid)

    for pid, pName in pidList:
        enumProcWnds(pid)
        if t:
            for title in t:
                titles.append("('{0}', '{1}')".format(pName, title))
            t = []
    titles = sorted(titles, key=lambda x: x[0].lower())
    return titles


def save_win(filename=None, title=None, crop=True):
    """
    Saves a window with the title provided as a file using the provided filename.
    If one of them is missing, then a window is created and the information collected

    :param filename:
    :param title:
    :return:
    """
    C = 7 if crop else 0  # pixels to crop
    try:
        fceuxHWND = win32gui.FindWindow(None, title)
        rect = win32gui.GetWindowRect(fceuxHWND)
        rect_cropped = (rect[0] + C, rect[1], rect[2] - C, rect[3] - C)
        frame = np.array(ImageGrab.grab(bbox=rect_cropped), dtype=np.uint8)
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        cv2.imwrite(filename, frame)
        sg.cprint('Wrote image to file:')
        sg.cprint(filename, c='white on purple')
    except Exception as e:
        sg.popup('Error trying to save screenshot file', e, keep_on_top=True)


def main():
    layout = [[sg.Text('Window Snapshot', key='-T-', font='Any 20', justification='c')],
              [sg.Text('Choose one or more window titles from list')],
              [sg.Listbox(values=[' '], size=(50, 20), select_mode=sg.SELECT_MODE_EXTENDED, font=('Courier', 12), key='-PROCESSES-')],
              [sg.Checkbox('Show only Python programs', default=True, key='-PYTHON ONLY-')],
              [sg.Checkbox('Crop image', default=True, key='-CROP-')],
              [sg.Multiline(size=(63, 10), font=('Courier', 10), key='-ML-')],
              [sg.Text('Output folder:', size=(15,1)), sg.In(os.path.dirname(__file__), key='-FOLDER-'), sg.FolderBrowse()],
              [sg.Text('Hardcode filename:', size=(15,1)), sg.In(key='-HARDCODED FILENAME-')],
              [sg.Button('Refresh'),
               sg.Button('Snapshot', button_color=('white', 'DarkOrange2')),
               sg.Exit(button_color=('white', 'sea green'))]]

    window = sg.Window('Window Snapshot', layout, keep_on_top=True, auto_size_buttons=False, default_button_element_size=(12, 1), finalize=True)

    window['-T-'].expand(True, False, False)  # causes text to center by expanding the element

    sg.cprint_set_output_destination(window, '-ML-')
    show_list_by_name(window, '-PROCESSES-', True)

    # ----------------  main loop  ----------------
    while True:
        # --------- Read and update window --------
        event, values = window.read()
        if event in (sg.WIN_CLOSED, 'Exit'):
            break

        # --------- Do Button Operations --------
        if event == 'Refresh':
            show_list_by_name(window, '-PROCESSES-', values['-PYTHON ONLY-'])
        elif event == 'Snapshot':
            for i, title in enumerate(values['-PROCESSES-']):
                sg.cprint('Saving:', end='', c='white on red')
                sg.cprint(' ', title, colors='white on green')
                if values['-HARDCODED FILENAME-']:
                    fname = values['-HARDCODED FILENAME-']
                    fname = f'{fname[:-4]}{i}{fname[-4:]}'
                    output_filename = os.path.join(values['-FOLDER-'], fname)
                else:
                    output_filename = os.path.join(values['-FOLDER-'], f'{title}.png')
                save_win(output_filename, title, values['-CROP-'])
    window.close()


if __name__ == "__main__":
    main()
